home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Ham Radio 2000
/
Ham Radio 2000.iso
/
ham2000
/
tcp_ip
/
os2
/
pmnos11s
/
kernel.c
< prev
next >
Wrap
Text File
|
1993-09-08
|
20KB
|
779 lines
/* Non pre-empting synchronization kernel, machine-independent portion
* Copyright 1991 Phil Karn, KA9Q
Preemptive kernel copyright 1991, 1992, 1993 Walt Corey KZ1F
*/
#define SUSPEND_PROC 1
#undef PROCTRACE /* kernel debugging */
#undef PROCLOG /* debugging */
#if defined(OS2)
#define INCL_BASE
#define INCL_DOS
#define INCL_WIN
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#include <os2.h>
#endif
#if defined(PROCLOG) || defined(PROCTRACE)
#include <stdio.h>
#endif
#if defined(OS2)
#include <stdlib.h>
#include <process.h>
#include <setjmp.h>
#include <stdio.h>
#include <stddef.h> /* for _threadid */
#define UM_ERROR WM_USER + 107
#else
#include <dos.h>
#include <setjmp.h>
#endif
#include "global.h"
#include "mbuf.h"
#include "proc.h"
#include "timer.h"
#include "socket.h"
#include "daemon.h"
#include "hardware.h"
#ifdef PROCLOG
FILE *proclog;
FILE *proctrace;
#endif
int Stkchk = 0;
struct proc *Curproc; /* Currently running process */
struct proc *Rdytab; /* Processes ready to run (not including curproc) */
struct proc *Waittab[PHASH]; /* Waiting process list */
struct proc *Susptab; /* Suspended processes */
static struct mbuf *Killq;
#if defined(OS2)
void * tss_array[100]; /* array for 100 thread-specific pointers */
APIRET APIENTRY MyExceptionHandler(EXCEPTIONREPORTRECORD *,
EXCEPTIONREGISTRATIONRECORD *,
CONTEXTRECORD *,
PVOID);
int fInCriticalSection; /* are we in a critical section */
static unsigned long semRdyWaitQ;
void strtTask(void * pv);
/*
Since we can have multiple tasks running and the existing system
is based on a global to specify the proc strcture addr, we will
change that to return same based on task id
*/
#endif
static void addproc __ARGS((struct proc *entry));
static void delproc __ARGS((struct proc *entry));
/* Create a process descriptor for the main function. Must be actually
* called from the main function!
* Note that standard I/O is NOT set up here.
*/
struct proc *
mainproc(name)
char *name;
{
register struct proc *pp;
register unsigned k;
register int16 *p;
/* Create process descriptor */
pp = (struct proc *)callocw(1,sizeof(struct proc));
/* Create name */
pp->name = strdup(name);
#if !defined(AMIGA) && !defined(OS2)
pp->stksize = _stklen / sizeof(int16);
pp->stack = (p = MK_FP(_SS,0));
k = _SP / sizeof(int16);
while(k-- > 0) *p++ = STACKPAT;
#endif
#if defined(OS2)
pp->stksize = 0;
#endif
#if defined(AMIGA)
init_psetup(pp);
#endif
/* Make current */
pp->state = READY;
Curproc = pp;
#if defined(OS2)
DosCreateEventSem(NULL, &pp->sem, 0, TRUE);
#endif
#ifdef PROCLOG
proclog = fopen("proclog",APPEND_TEXT);
proctrace = fopen("proctrace",APPEND_TEXT);
#endif
return pp;
}
/* Create a new, ready process and return pointer to descriptor.
* The general registers are not initialized, but optional args are pushed
* on the stack so they can be seen by a C function.
*/
struct proc *
newproc(name,stksize,pc,iarg,parg1,parg2,freeargs)
char *name; /* Arbitrary user-assigned name string */
unsigned int stksize; /* Stack size in words to allocate */
void (*pc)(); /* Initial execution address */
int iarg; /* Integer argument (argc) */
void *parg1; /* Generic pointer argument #1 (argv) */
void *parg2; /* Generic pointer argument #2 (session ptr) */
int freeargs; /* If set, free arg list on parg1 at termination */
{
register struct proc *pp;
int i;
#if defined(OS2)
TID tid;
#else
if(Stkchk)
chkstk();
#endif
/* Create process descriptor */
pp = (struct proc *)callocw(1,sizeof(struct proc));
/* Create name */
pp->name = strdup(name);
/* Allocate stack */
#if defined(AMIGA)
stksize += 2000; /* DOS overhead */
#endif
pp->stksize = stksize;
pp->stack = NULL;
#if !defined(OS2)
if((pp->stack = (int16 *)malloc(sizeof(int16)*stksize)) == NULL){
free(pp->name);
free((char *)pp);
return NULLPROC;
}
/* Initialize stack for high-water check */
for(i=0;i<stksize;i++)
pp->stack[i] = STACKPAT;
#endif
/* Do machine-dependent initialization of stack */
psetup(pp,iarg,parg1,parg2,pc);
pp->freeargs = freeargs;
pp->iarg = iarg;
pp->parg1 = parg1;
pp->parg2 = parg2;
/* Inherit creator's input and output sockets */
usesock(Curproc->input);
pp->input = Curproc->input;
usesock(Curproc->output);
pp->output = Curproc->output;
#if defined(OS2)
pp->pc = pc;
#endif
/* Add to ready process table */
pp->state = READY;
#if defined(OS2)
i = pp->stksize * sizeof(int16);
i -= i % 8192;
i += 8192;
pp->stksize = 0;
tid = (TID)_beginthread(strtTask,
pp->stack,
i,
pp);
DosSleep(100); /* give it a chance to start */
#else
addproc(pp);
#endif
return pp;
}
#if defined(OS2)
void killproc(struct proc *pp)
{
char **argv;
if (pp == NULLPROC) /* dont know why this would be but...*/
return;
pp->fTerminate = 1; /* mark for termination */
psignal(pp, 0); /* wake up those waiting for this to die */
stop_timer(&pp->alarm);
freesock(pp);
close_s(pp->input);
close_s(pp->output);
if (Curproc != pp) /* dont queue ourselves */
alert(pp, EABORT);
/* Free allocated memory resources */
if(pp->freeargs){
argv = pp->parg1;
while(pp->iarg-- != 0)
free(*argv++);
free(pp->parg1);
}
free(pp->name);
free(pp->outbuf);
return;
}
void killself()
{
killproc(Curproc);
while (1)
pwait(NULL);
}
/* Process used by processes that want to kill themselves */
void
killer(i,v1,v2)
int i;
void *v1;
void *v2;
{
struct proc *pp;
struct mbuf *bp;
for(;;){
while(Killq == NULLBUF)
pwait(&Killq);
bp = dequeue(&Killq);
pullup(&bp,(char *)&pp,sizeof(pp));
free_p(bp);
if(pp != Curproc) { /* We're immortal */
/* free(pp->stack);*/
free((char *)pp);
}
}
}
#else
/* Free resources allocated to specified process. If a process wants to kill */
* itself, the reaper is called to do the dirty work. This avoids some
* messy situations that would otherwise occur, like freeing your own stack.
*/
void
killproc(pp)
register struct proc *pp;
{
char **argv;
if(pp == NULLPROC)
return;
/* Don't check the stack here! Will cause infinite recursion if
* called from a stack error
*/
if(pp == Curproc)
killself(); /* Doesn't return */
/* Close any open sockets */
freesock(pp);
close_s(pp->input);
close_s(pp->output);
/* Stop alarm clock in case it's running */
stop_timer(&pp->alarm);
/* Alert everyone waiting for this proc to die */
psignal(pp,0);
/* Remove from appropriate table */
delproc(pp);
#ifdef PROCLOG
fprintf(proclog,"id %lx name %s stack %u/%u\n",ptol(pp),
pp->name,stkutil(pp),pp->stksize);
fclose(proclog);
proclog = fopen("proclog",APPEND_TEXT);
proctrace = fopen("proctrace",APPEND_TEXT);
#endif
/* Free allocated memory resources */
if(pp->freeargs){
argv = pp->parg1;
while(pp->iarg-- != 0)
free(*argv++);
free(pp->parg1);
}
free(pp->name);
#if !defined(OS2)
free(pp->stack);
#endif
free(pp->outbuf);
#if !defined(OS2)
free((char *)pp);
#endif
}
/* Terminate current process by sending a request to the killer process.
* Automatically called when a process function returns. Does not return.
*/
void
killself()
{
register struct mbuf *bp;
if(Curproc != NULLPROC){
bp = pushdown(NULLBUF,sizeof(Curproc));
memcpy(bp->data,(char *)&Curproc,sizeof(Curproc));
enqueue(&Killq,bp);
}
/* "Wait for me; I will be merciful and quick." */
for(;;)
pwait(NULL);
}
/* Process used by processes that want to kill themselves */
void
killer(i,v1,v2)
int i;
void *v1;
void *v2;
{
struct proc *pp;
struct mbuf *bp;
for(;;){
while(Killq == NULLBUF)
pwait(&Killq);
bp = dequeue(&Killq);
pullup(&bp,(char *)&pp,sizeof(pp));
free_p(bp);
if(pp != Curproc) /* We're immortal */
{
killproc(pp);
#if defined(OS2)
free(pp->stack);
free((char *)pp);
}
#endif
}
}
#endif
#ifdef SUSPEND_PROC
/* Inhibit a process from running */
void
suspend(pp)
struct proc *pp;
{
if(pp == NULLPROC)
return;
if(pp != Curproc)
delproc(pp); /* Running process isn't on any list */
pp->state |= SUSPEND;
if(pp != Curproc)
addproc(pp); /* pwait will do it for us */
else
pwait(NULL);
}
/* Restart suspended process */
void
resume(pp)
struct proc *pp;
{
if(pp == NULLPROC)
return;
delproc(pp); /* Can't be Curproc! */
pp->state &= ~SUSPEND;
addproc(pp);
}
#endif /* SUSPEND_PROC */
/* Wakeup waiting process, regardless of event it's waiting for. The process
* will see a return value of "val" from its pwait() call.
*/
void
alert(pp,val)
struct proc *pp;
int val;
{
if(pp == NULLPROC)
return;
#ifdef notdef
if((pp->state & WAITING) == 0)
return;
#endif
#ifdef PROCTRACE
tprintf("alert(%lx,%u) [%s]\n",ptol(pp),val,pp->name);
fflush(stdout);
#endif
if(pp != Curproc)
delproc(pp);
pp->state &= ~WAITING;
pp->retval = val;
pp->event = 0;
if(pp != Curproc)
addproc(pp);
}
/* Post a wait on a specified event and give up the CPU until it happens. The
* null event is special: it means "I don't want to block on an event, but let
* somebody else run for a while". It can also mean that the present process
* is terminating; in this case the wait never returns.
*
* Pwait() returns 0 if the event was signaled; otherwise it returns the
* arg in an alert() call. Pwait must not be called from interrupt level.
*
* Note that pwait can run with interrupts enabled even though it examines
* a few global variables that can be modified by psignal at interrupt time.
* These *seem* safe.
*/
extern int fInterruptDepth;
int
pwait(event)
void *event;
{
#if defined(OS2)
struct mbuf *bp;
ULONG ulPostCt;
#endif
struct proc *oldproc;
int tmp;
extern int WDTick;
extern int WDCurr;
WDCurr = WDTick; /* reset watchdog timer value */
if(Curproc != NULLPROC){ /* If process isn't terminating */
#if !defined(OS2)
if(Stkchk)
chkstk();
#endif
if(event == NULL)
{
/* Special case; just give up the processor.
*
* Optimization: if nothing else is ready, just return.
*/
if(Rdytab == NULLPROC)
return 0;
}
else
{
/* Post a wait for the specified event */
Curproc->event = event;
Curproc->state = WAITING;
}
#if defined(OS2)
DosEnterCritSec();
if (!Curproc->fTerminate)
#endif
addproc(Curproc);
#if defined(OS2)
DosExitCritSec();
#endif
}
/* Look for a ready process and run it. If there are none,
* loop or halt until an interrupt makes something ready.
*/
while(Rdytab == NULLPROC){
/* Give system back to upper-level multitasker, if any.
* Note that this function enables interrupts internally
* to prevent deadlock, but it restores our state
* before returning.
*/
#if !defined(OS2)
kbint(); /***/
#endif
giveup();
}
/* Remove first entry from ready list */
DosEnterCritSec();
oldproc = Curproc;
Curproc = Rdytab;
delproc(Curproc);
DosExitCritSec();
#if !defined(OS2)
/* Now do the context switch.
* This technique was inspired by Rob, PE1CHL, and is a bit tricky.
*
* If the old process has gone away, simply load the new process's
* environment. Otherwise, save the current process's state. Then if
* this is still the old process, load the new environment. Since the
* new task will "think" it's returning from the setjmp() with a return
* value of 1, the comparison with 0 will bypass the longjmp(), which
* would otherwise cause an infinite loop.
*/
#ifdef PROCTRACE
if(strcmp(oldproc->name,Curproc->name) != 0){
tprintf("pwait -> %s(%d)\n",Curproc->name,!!Curproc->i_state);
fflush(stdout);
}
#endif
/* Note use of comma operator to save old interrupt state only if
* oldproc is non-null
*/
if(oldproc == NULLPROC
|| (oldproc->i_state = istate(), setjmp(oldproc->env) == 0)){
/* We're still running in the old task; load new task context.
* The interrupt state is restored here in case longjmp
* doesn't do it (e.g., systems other than Turbo-C).
*/
restore(Curproc->i_state);
longjmp(Curproc->env,1);
}
#else
if (Curproc == oldproc) /* waiting on myself? */
return(0);
oldproc->i_state = istate();
if (fInterruptDepth)
tprintf("\nInterrupts are OFF!\n");
DosResetEventSem(oldproc->sem, &ulPostCt);
if (oldproc->fTerminate)
{
bp = pushdown(NULLBUF,sizeof(oldproc));
memcpy(bp->data,(char *)&oldproc,sizeof(oldproc));
enqueue(&Killq,bp);
DosPostEventSem(Curproc->sem);
_endthread();
}
DosPostEventSem(Curproc->sem);
DosWaitEventSem(oldproc->sem, SEM_INDEFINITE_WAIT);
Curproc = oldproc;
#endif
/* At this point, we're running in the newly dispatched task */
tmp = Curproc->retval;
Curproc->retval = 0;
/* Also restore the true interrupt state here, in case the longjmp
* DOES restore the interrupt state saved at the time of the setjmp().
* This is the case with Turbo-C's setjmp/longjmp.
*/
#if !defined(OS2)
restore(Curproc->i_state);
#endif
return tmp;
}
/* Make ready the first 'n' processes waiting for a given event. The ready
* processes will see a return value of 0 from pwait(). Note that they don't
* actually get control until we explicitly give up the CPU ourselves through
* a pwait(). Psignal may be called from interrupt level. It returns the
* number of processes that were woken up.
*/
int
psignal(event,n)
void *event; /* Event to signal */
int n; /* Max number of processes to wake up */
{
register struct proc *pp;
struct proc *pnext;
int i_state;
unsigned int hashval;
int cnt = 0;
if(Stkchk)
chkstk();
if(event == NULL)
return 0; /* Null events are invalid */
/* n = 0 means "signal everybody waiting for this event" */
if(n == 0)
n = 65535;
hashval = phash(event);
i_state = dirps();
for(pp = Waittab[hashval];n != 0 && pp != NULLPROC;pp = pnext){
pnext = pp->next;
if(pp->event == event){
#ifdef PROCTRACE
if(i_state){
tprintf("psignal(%lx,%u) wake %lx [%s]\n",ptol(event),n,
ptol(pp),pp->name);
fflush(stdout);
}
#endif
delproc(pp);
pp->state &= ~WAITING;
pp->retval = 0;
pp->event = NULL;
addproc(pp);
n--;
cnt++;
}
}
#ifdef SUSPEND_PROC
for(pp = Susptab;n != 0 && pp != NULLPROC;pp = pnext){
pnext = pp->next;
if(pp->event == event){
#ifdef PROCTRACE
if(i_state){
tprintf("psignal(%lx,%u) wake %lx [%s]\n",ptol(event),n,
ptol(pp),pp->name);
fflush(stdout);
}
#endif /* PROCTRACE */
delproc(pp);
pp->state &= ~WAITING;
pp->event = 0;
pp->retval = 0;
addproc(pp);
n--;
cnt++;
}
}
#endif /* SUSPEND_PROC */
restore(i_state);
return cnt;
}
/* Rename a process */
void
chname(pp,newname)
struct proc *pp;
char *newname;
{
free(pp->name);
pp->name = strdup(newname);
#if defined(PM)
if (Current)
WinSetWindowText(Current->hwndFrame, pp->name);
#endif
}
/* Remove a process entry from the appropriate table */
static void
delproc(entry)
register struct proc *entry; /* Pointer to entry */
{
int i_state;
if(entry == NULLPROC)
return;
i_state = dirps();
if(entry->next != NULLPROC)
entry->next->prev = entry->prev;
if(entry->prev != NULLPROC){
entry->prev->next = entry->next;
} else {
switch(entry->state){
case READY:
Rdytab = entry->next;
break;
case WAITING:
Waittab[phash(entry->event)] = entry->next;
break;
#ifdef SUSPEND_PROC
case SUSPEND:
case SUSPEND|WAITING:
Susptab = entry->next;
break;
#endif
}
}
restore(i_state);
}
/* Append proc entry to end of appropriate list */
static void
addproc(entry)
register struct proc *entry; /* Pointer to entry */
{
register struct proc *pp;
struct proc **head;
int i_state;
if(entry == NULLPROC)
return;
switch(entry->state){
case READY:
head = &Rdytab;
break;
case WAITING:
head = &Waittab[phash(entry->event)];
break;
#ifdef SUSPEND_PROC
case SUSPEND:
case SUSPEND|WAITING:
head = &Susptab;
break;
#endif
}
entry->next = NULLPROC;
i_state = dirps();
if(*head == NULLPROC){
/* Empty list, stick at beginning */
entry->prev = NULLPROC;
*head = entry;
} else {
/* Find last entry on list */
for(pp = *head;pp->next != NULLPROC;pp = pp->next)
;
pp->next = entry;
entry->prev = pp;
}
restore(i_state);
}
#if defined(OS2)
#pragma map(_Exception,"MyExceptionHandler")
#pragma handler(strtTask)
void strtTask(void *pv)
{
void *pvArgs[2];
int i_state;
HAB hab1;
jmp_buf jbuf; /* put the jump buffer in automatic storage */
/* so it is unique to this thread */
PTIB ptib; /* to get the TIB pointer */
PPIB ppib;
struct proc *pp = (struct proc *)pv;
unsigned int tid = *_threadid; /* get the thread id */
/* create a thread specific jmp_buf */
tss_array[tid] = (void *) jbuf;
if (!setjmp(jbuf)) /* provide a point to return to */
{
DosSetPrty(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
addproc(pp);
DosCreateEventSem(NULL, &pp->sem, 0, FALSE);
DosWaitEventSem(pp->sem, -1);
DosSetPrty(PRTYS_THREAD, PRTYC_REGULAR, 0, 0);
pvArgs[0] = pp->parg1;
pvArgs[1] = pp->parg2;
(*pp->pc)(pp->iarg, pp->parg1, pp->parg2);
}
killself();
return;
}
extern HWND hwndCmd;
APIRET APIENTRY MyExceptionHandler(EXCEPTIONREPORTRECORD * report_rec,
EXCEPTIONREGISTRATIONRECORD * register_rec,
CONTEXTRECORD * context_rec,
PVOID dummy)
{
void *mp1;
void *mp2;
unsigned int tid; /* get the thread id */
tid = *_threadid;
/* check the exception flags */
if (EH_EXIT_UNWIND & report_rec->fHandlerFlags) /* exiting */
return XCPT_CONTINUE_SEARCH;
if (EH_UNWINDING & report_rec->fHandlerFlags) /* unwinding */
return XCPT_CONTINUE_SEARCH;
if (EH_NESTED_CALL & report_rec->fHandlerFlags) /* nested exceptions */
return XCPT_CONTINUE_SEARCH;
/* determine what the exception is */
if (report_rec->ExceptionNum == XCPT_ACCESS_VIOLATION)
/* this is the one that is expected */
{
mp1 = malloc(sizeof(struct proc));
memcpy(mp1, Curproc, sizeof(struct proc));
mp2 = malloc(sizeof(EXCEPTIONREPORTRECORD));
memcpy(mp2, report_rec, sizeof(EXCEPTIONREPORTRECORD));
WinPostMsg(hwndCmd, UM_ERROR, MPFROMP(mp1), MPFROMP(mp2));
longjmp((int *)tss_array[tid],1); /* return to the point of the */
/* setjmp call without */
/* restarting the process */
} /* endif */
return XCPT_CONTINUE_SEARCH; /* if it is a different exception */
}
#endif